package com.bigknow.appstore.proxy.handler.dispatcher;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.entity.ProxyResourceMeta;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorAssert;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorHelper;
import com.bigknow.appstore.proxy.handler.base.BaseOutboundHandler;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.util.UriAndUrlHelper;
import com.bigknow.appstore.proxy.vo.RequestVo;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpResponse;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.UnsupportedEncodingException;

/**
 * 读取response的消息并判断地址是否是采用的原始服务的绝对地址（http://XXXXX）
 * 如果是就将地址转换为能够获取到的proxyResource资源的proxy地址。
 * Created by chaos on 2016/9/20.
 */
@ChannelHandler.Sharable
public class ResponseDispatcherHandler extends BaseOutboundHandler {

    public ResponseDispatcherHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        super(proxyServer, cacheManager, writer);
    }

    @Override
    public void write(ChannelHandlerContext ctx, FullHttpMessage msg, ChannelPromise promise) {
        ErrorAssert.assertFullHttpResponseMsg(ctx, msg, HandlerVariables.requestVoForCurrentResponse.get(ctx.channel().id().asLongText()));
        //开启此功能的资源才执行内容重定向

        if (getRequestVo(ctx).getResourceMeta().isUseReponseDispatcher()) {
            FullHttpResponse response = (FullHttpResponse) msg;
            String contentType = response.headers().get("Content-Type");
            if (contentType != null) {
                String[] typeArray = contentType.split(";");
                if (typeArray[0].equals("text/html")) {
                    String encoding = getEncoding(typeArray, ctx);
                    //读取content
                    int bytelength = response.content().readableBytes();
                    if (bytelength != 0) {
                        byte[] req = new byte[bytelength];
                        response.content().readBytes(req);
                        try {
                            Document doc = Jsoup.parse(new String(req, encoding));
                            replaceAs(ctx, doc);
                            replaceLinks(ctx, doc);
                            replaceImgs(ctx, doc);
                            replaceScript(ctx, doc);
                            byte[] content = doc.html().getBytes(encoding);
                            response.content().writeBytes(content);
                            response.headers().set("Content-Length", content.length);
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }

        ctx.write(msg, promise);
    }

    /**
     * 发现并替换所有的<script></script>元素
     *
     * @param doc
     */
    private void replaceScript(ChannelHandlerContext ctx, Document doc) {
        replaceByPram(ctx, doc, "script", "src");
    }

    /**
     * 发现并替换所有的<a>元素
     *
     * @param doc
     */
    private void replaceAs(ChannelHandlerContext ctx, Document doc) {
        replaceByPram(ctx, doc, "a", "href");
    }

    /**
     * 发现并替换所有link元素
     *
     * @param doc
     */
    private void replaceLinks(ChannelHandlerContext ctx, Document doc) {
        replaceByPram(ctx, doc, "link", "href");
    }

    /**
     * 发现并替换所有img元素
     *
     * @param doc
     */
    private void replaceImgs(ChannelHandlerContext ctx, Document doc) {
        replaceByPram(ctx, doc, "img", "src");
    }

    private void replaceByPram(ChannelHandlerContext ctx, Document doc, String tagName, String attr) {
        Elements tags = doc.getElementsByTag(tagName);
        for (Element tag : tags) {
            String url = tag.attr(attr);
            if (url.startsWith("http") || url.startsWith("https")) {
                String currentUrl = findAndCreateCurrentUrl(url);
                if (currentUrl != null) {
                    tag.attr(attr, currentUrl);
                }
                //绝对路径的uri，必须替换
            } else if (url.startsWith("/")) {
                RequestVo requestVo = HandlerVariables.requestVoForCurrentResponse.get(ctx.channel().id().asLongText());
                if (!(requestVo.getResourceMeta().getSourcePath() == null
                        || requestVo.getResourceMeta().getSourcePath().equals("")
                        || requestVo.getResourceMeta().getSourcePath().equals("/"))) {
                    //uri截取
                    url = UriAndUrlHelper.getCorrectUri(requestVo.getResourceMeta().getSourcePath(), url);
                }
                StringBuilder builder = new StringBuilder();
                builder.append(proxyServer.getProxyMeta().isHTTPS() ? "https://" : "http://").append(proxyServer.getRealServerIp())
                        .append(":")
                        .append(proxyServer.getProxyMeta().getPort())
                        .append(requestVo.getResourceMeta().getProxyUri())
                        .append(url);
                tag.attr(attr, builder.toString());
            }
        }
    }


    private String getEncoding(String[] contentType, ChannelHandlerContext ctx) {
        if (contentType.length <= 1) {
            return getDefaultEncoding(ctx);
        }
        try {
            String encoding = contentType[1].substring(contentType[1].indexOf("=") + 1, contentType[1].length());
            return encoding.equals("") ? getDefaultEncoding(ctx) : encoding;
        } catch (Exception e) {
            return getDefaultEncoding(ctx);
        }
    }

    private String getDefaultEncoding(ChannelHandlerContext ctx) {
        ProxyResourceMeta proxyResourceMeta = getRequestVo(ctx).getResourceMeta();
        if (proxyResourceMeta.getDefaultEncoding() == null) {
            ErrorHelper.throwResponseEncodingError(ctx.channel(), getRequestVo(ctx));
        }
        return proxyResourceMeta.getDefaultEncoding();
    }

}
